package utils

import (
	"fmt"
	"image"
	_ "image/gif"
	_ "image/jpeg"
	"image/png"
	_ "image/png"
	"io"
	"math"
	"mime/multipart"
	"os"
	"strings"
	"time"

	"gitee.com/sansaniot/ssiot-core/httpmvc/api"
	"gitee.com/sansaniot/ssiot-core/logger"
	"gitee.com/sansaniot/ssiot-core/utils"
	"github.com/gin-gonic/gin"
	"github.com/nfnt/resize"

	"ssadmin/common/constant"
)

type FileInfo struct {
	Size         int64  `json:"size"`
	Path         string `json:"path"`
	OriginalName string `json:"name"`
	Type         string `json:"type"`
	Width        float64
	Height       float64
}

type HttpFile struct {
	api.Api
	FilePath string
}

// 上传图片
func (e *HttpFile) UploadFile(c *gin.Context, filePath string, force ...bool) (fileInfos []FileInfo, err error) {
	e.FilePath = filePath
	fileInfos = make([]FileInfo, 0)
	if err = utils.IsNotExistMkDir(e.FilePath); err != nil {
		return
	}
	if len(c.Request.MultipartForm.File["file"]) > 1 {
		if fileInfos, err = e.multipleFile(c, len(force) > 0 && force[0]); err != nil {
			return
		}
	} else {
		var fileResponse FileInfo
		if fileResponse, err = e.singleFile(c, len(force) > 0 && force[0]); err != nil {
			return
		}
		fileInfos = append(fileInfos, fileResponse)
	}
	return
}

func (e *HttpFile) multipleFile(c *gin.Context, force bool) ([]FileInfo, error) {
	var fileInfos []FileInfo
	files := c.Request.MultipartForm.File["file"]
	for _, file := range files {
		// 上传文件至指定目录
		uniqueName := file.Filename
		if !force {
			uniqueName = uniqueFileName(file.Filename)
		}
		wholeFileName := e.FilePath + "/" + uniqueName
		fileInfo, err := e.saveGetFileInfo(file, wholeFileName, force)
		if err != nil {
			return fileInfos, err
		}
		fileInfos = append(fileInfos, fileInfo)
	}
	return fileInfos, nil
}

func (e *HttpFile) singleFile(c *gin.Context, force bool) (FileInfo, error) {
	file, err := c.FormFile("file")
	if err != nil {
		return FileInfo{}, err
	}
	// 上传文件至指定目录
	uniqueName := file.Filename
	if !force {
		uniqueName = uniqueFileName(file.Filename)
	}
	wholeFileName := e.FilePath + "/" + uniqueName
	fileInfo, err := e.saveGetFileInfo(file, wholeFileName, force)
	if err != nil {
		return fileInfo, err
	}
	return fileInfo, nil
}

func (e *HttpFile) saveGetFileInfo(file *multipart.FileHeader, wholeFileName string, force bool) (fileInfo FileInfo, err error) {
	var src multipart.File
	if src, err = file.Open(); err != nil {
		return
	}
	defer src.Close()
	if force {
		_ = os.Remove(wholeFileName)
	}
	// 保存图片
	var out *os.File
	if out, err = os.Create(wholeFileName); err != nil {
		return
	}
	defer out.Close()
	if _, err = io.Copy(out, src); err != nil {
		return
	}
	fileType, _ := utils.GetType(wholeFileName)
	// 图片信息
	fileInfo = FileInfo{
		Size:         file.Size,
		Path:         wholeFileName,
		OriginalName: file.Filename,
		Type:         fileType,
	}
	if strings.Contains(fileType, "image") || strings.LastIndex(wholeFileName, ".svg") > 0 {
		// 获取图片大小(需要新读取文件)
		tmpFile, _ := os.Open(wholeFileName)
		defer tmpFile.Close()
		img, _, er := image.Decode(tmpFile)
		if er != nil {
			logger.Error("提取图片大小失败", er)
		} else {
			fileInfo.Width = float64(img.Bounds().Max.X)
			fileInfo.Height = float64(img.Bounds().Max.Y)
			// 存一份缩略图
			saveThumbnail(img, fileInfo)
		}
	}
	return
}

func uniqueFileName(fileName string) string {
	suffix := utils.GetExt(fileName)
	withoutSuffix := strings.ReplaceAll(fileName, suffix, "")
	fileName = fmt.Sprintf("%s_%s%s", withoutSuffix, formatDate(time.Now(), "yyyyMMddHHmmssSSS"), suffix)
	return fileName
}

func saveThumbnail(img image.Image, fileInfo FileInfo) {
	// 等比例缩略图
	ratio := math.Min(100/fileInfo.Width, 100/fileInfo.Height)
	w := int(math.Ceil(fileInfo.Width) * ratio)
	h := int(math.Ceil(fileInfo.Height) * ratio)
	thumbnailImage := resize.Resize(uint(w), uint(h), img, resize.Lanczos3)
	// 需要保存的文件
	suffix := utils.GetExt(fileInfo.Path)
	withoutSuffix := strings.ReplaceAll(fileInfo.Path, suffix, "")
	thumbnailImagePath := withoutSuffix + constant.ThumbnailConcat + suffix
	thumbImageFile, err := os.Create(thumbnailImagePath)
	defer thumbImageFile.Close()
	// 保存文件
	err = png.Encode(thumbImageFile, thumbnailImage)
	if err != nil {
		logger.Error("生成缩略图失败", err)
	}
}
